package com.bolt.support.freemarker;

import com.bolt.common.convert.Convert;
import com.bolt.common.utils.DateUtil;
import com.bolt.common.utils.StrUtil;
import com.bolt.convention.data.code.CommonCode;
import com.bolt.convention.exception.ServiceException;
import com.bolt.convention.exception.UtilException;
import freemarker.core.Environment;
import freemarker.ext.beans.StringModel;
import freemarker.ext.servlet.*;
import freemarker.template.*;
import org.springframework.web.servlet.support.RequestContext;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.*;

import static org.springframework.web.servlet.view.AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE;

/**
 * Created by Administrator on 2019/4/17.
 */
public class DirectiveUtil {

    public static final String ENCODING = "UTF-8";
    public static final String REQUEST_CONTEX = "request";
    public static final String REQUEST = "Request";

    /**
     * 将params的值复制到variable中
     *
     * @param env
     * @param params
     * @return 原Variable中的值
     * @throws TemplateException
     */
    public static Map<String, TemplateModel> addParamsToVariable(
            Environment env, Map<String, TemplateModel> params)
            throws TemplateException {
        Map<String, TemplateModel> origMap = new HashMap<String, TemplateModel>();
        if (params.size() <= 0) {
            return origMap;
        }
        Set<Map.Entry<String, TemplateModel>> entrySet = params.entrySet();
        String key;
        TemplateModel value;
        for (Map.Entry<String, TemplateModel> entry : entrySet) {
            key = entry.getKey();
            value = env.getVariable(key);
            if (value != null) {
                origMap.put(key, value);
            }
            env.setVariable(key, entry.getValue());
        }
        return origMap;
    }

    /**
     * 将variable中的params值移除
     *
     * @param env
     * @param params
     * @param origMap
     * @throws TemplateException
     */
    public static void removeParamsFromVariable(Environment env,
                                                Map<String, TemplateModel> params,
                                                Map<String, TemplateModel> origMap) throws TemplateException {
        if (params.size() <= 0) {
            return;
        }
        for (String key : params.keySet()) {
            env.setVariable(key, origMap.get(key));
        }
    }

    /**
     * 获得RequestContext
     * <p>
     * ViewResolver中的exposeSpringMacroHelpers必须为true
     *
     * @param env
     * @return
     * @throws TemplateException
     */
    public static RequestContext getContext(Environment env)
            throws TemplateException {
        TemplateModel ctx = env
                .getGlobalVariable(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
        if (ctx instanceof AdapterTemplateModel) {
            return (RequestContext) ((AdapterTemplateModel) ctx)
                    .getAdaptedObject(RequestContext.class);
        } else {
            throw new TemplateModelException("RequestContext '"
                    + SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE
                    + "' not found in DataModel.");
        }
    }

    public static String getString(Map<String, TemplateModel> params, String name) {
        try {
            TemplateModel model = params.get(name);
            if (model == null) {
                return null;
            }
            if (model instanceof TemplateScalarModel) {
                return ((TemplateScalarModel) model).getAsString();
            } else if ((model instanceof TemplateNumberModel)) {
                return ((TemplateNumberModel) model).getAsNumber().toString();
            } else {
                return null;
            }
        } catch (TemplateModelException ex) {
            throw new ServiceException(CommonCode.UNKNOWN_ERROR, ex);
        }

    }

    public static Long getLong(Map<String, TemplateModel> params, String name, long defaultValue) {
        try {
            TemplateModel model = params.get(name);
            if (model == null) {
                return defaultValue;
            }
            if (model instanceof TemplateScalarModel) {
                String s = ((TemplateScalarModel) model).getAsString();
                return Convert.toLong(s, defaultValue);
            } else if (model instanceof TemplateNumberModel) {
                return ((TemplateNumberModel) model).getAsNumber().longValue();
            } else {
                return defaultValue;
            }
        } catch (TemplateModelException ex) {
            throw new ServiceException(CommonCode.UNKNOWN_ERROR, ex);
        }
    }

    public static Integer getInt(Map<String, TemplateModel> params, String name, int defaultValue) {
        try {
            TemplateModel model = params.get(name);
            if (model == null) {
                return defaultValue;
            }
            if (model instanceof TemplateScalarModel) {
                String s = ((TemplateScalarModel) model).getAsString();
                return Convert.toInt(s, defaultValue);
            } else if (model instanceof TemplateNumberModel) {
                return ((TemplateNumberModel) model).getAsNumber().intValue();
            } else {
                return defaultValue;
            }
        } catch (TemplateModelException ex) {
            throw new ServiceException(CommonCode.UNKNOWN_ERROR, ex);
        }


    }

    public static Integer[] getIntArray(Map<String, TemplateModel> params, String name) {
        String str = DirectiveUtil.getString(params, name);
        if (StrUtil.isBlank(str)) {
            return null;
        }
        String[] arr = StrUtil.splitToArray(str, ',');
        Integer[] ids = new Integer[arr.length];
        int i = 0;
        try {
            for (String s : arr) {
                ids[i++] = Integer.valueOf(s);
            }
            return ids;
        } catch (NumberFormatException e) {
            return null;
        }
    }

    public static Boolean getBoolean(Map<String, TemplateModel> params, String name, boolean defaultValue) {
        try {
            TemplateModel model = params.get(name);
            if (model == null) {
                return defaultValue;
            }
            if (model instanceof TemplateBooleanModel) {
                return ((TemplateBooleanModel) model).getAsBoolean();
            } else if (model instanceof TemplateNumberModel) {
                return !(((TemplateNumberModel) model).getAsNumber().intValue() == 0);
            } else if (model instanceof TemplateScalarModel) {
                String s = ((TemplateScalarModel) model).getAsString();
                return Convert.toBool(s, defaultValue);
            } else {
                return defaultValue;
            }
        } catch (TemplateModelException ex) {
            throw new ServiceException(CommonCode.UNKNOWN_ERROR, ex);
        }
    }

    public static Date getDate(Map<String, TemplateModel> params, String name) {
        try {
            TemplateModel model = params.get(name);
            if (model == null) {
                return null;
            }
            if (model instanceof TemplateDateModel) {
                return ((TemplateDateModel) model).getAsDate();
            } else if (model instanceof TemplateScalarModel) {
                String s = (((TemplateScalarModel) model).getAsString());
                return DateUtil.parseDate(s);
            } else {
                return null;
            }
        } catch (TemplateModelException ex) {
            throw new ServiceException(CommonCode.UNKNOWN_ERROR, ex);
        }
    }

    public static Set<String> getKeysByPrefix(Map<String, TemplateModel> params, String prefix) {
        Set<String> keys = params.keySet();
        Set<String> startWithPrefixKeys = new HashSet<String>();
        if (keys == null) {
            return null;
        }
        for (String key : keys) {
            if (key.startsWith(prefix)) {
                startWithPrefixKeys.add(key);
            }
        }
        return startWithPrefixKeys;
    }

    public static RequestContext getRequestContext(Environment env) {
        StringModel val = (StringModel) getVariable(env, REQUEST_CONTEX, true);
        return (RequestContext) val.getWrappedObject();
    }

    public static HttpServletRequest getRequest(Environment env) {
        HttpRequestHashModel req = (HttpRequestHashModel) getVariable(env, REQUEST, true);
        return req.getRequest();
    }


    public static TemplateModel getVariable(Environment env, String name, boolean throwIfError) {
        TemplateModel model = null;
        try {
            model = env.getVariable(name);
        } catch (Exception e) {
            if (throwIfError)
                throw new UtilException("get variable[" + name + "] error: " + e.getMessage(), e);
        }
        return model;
    }

    public static SimpleHash buildTemplateModel(Configuration cfg, HttpServletRequest request, HttpServletResponse response) {
        ObjectWrapper objectWrapper = getObjectWrapper(cfg);
        AllHttpScopesHashModel fmModel = new AllHttpScopesHashModel(objectWrapper, request.getServletContext(), request);
        fmModel.put(FreemarkerServlet.KEY_APPLICATION, new ServletContextHashModel(request.getServletContext(), objectWrapper));
        fmModel.put(FreemarkerServlet.KEY_REQUEST, new HttpRequestHashModel(request, response, objectWrapper));
        fmModel.put(FreemarkerServlet.KEY_REQUEST_PARAMETERS, new HttpRequestParametersHashModel(request));
        fmModel.put(FreemarkerServlet.KEY_SESSION, buildSessionModel(request, response, objectWrapper));
        return fmModel;
    }


    private static HttpSessionHashModel buildSessionModel(HttpServletRequest request, HttpServletResponse response, ObjectWrapper objectWrapper) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            return new HttpSessionHashModel(session, objectWrapper);
        } else {
            return new HttpSessionHashModel(null, request, response, objectWrapper);
        }
    }


    private static ObjectWrapper getObjectWrapper(Configuration cfg) {
        ObjectWrapper ow = cfg.getObjectWrapper();
        return (ow != null ? ow : new DefaultObjectWrapperBuilder(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS).build());
    }

}

 